<?php


namespace pay;


use custom\Curl;
use custom\Err;

abstract class PayBase {

    /**
     * 配置文件
     * @var array
     */
    protected $configs = [];

    /**
     * 交易参数
     * @var array
     */
    protected $data = [];

    public function __construct($data, $config = []) {
        // 参数处理
        $this->setData($data);
        // 配置文件
        $this->setConfigs($config);
        $this->data = array_merge($this->data, $this->configs);
        unset($this->data['key']);
    }

    /**
     * 时间处理
     */
    protected function setTime() {
        // 交易起始时间
        $this->data['time_start'] = (string)date('YmdHis');
        // 交易结束时间
        $this->data['time_expire'] = (string)date('YmdHis', strtotime($this->data['time_start']) + ($this->configs['timeout'] ?? 1800));
    }

    /**
     * 生成签名
     */
    protected function makeSign() {
        //签名步骤一：按字典序排序参数
        ksort($this->data);
        $string = $this->dataToUrl();
        //签名步骤二：在string后加入KEY
        $string = $string."&key=".$this->configs['key'];
        //签名步骤三：MD5加密或者HMAC-SHA256
        if($this->configs['sign_type'] == 'MD5') {
            //如果签名小于等于32个,则使用md5验证
            $string = md5($string);
        } else {
            //是用sha256校验
            $string = hash_hmac("sha256", $string, $this->configs['key']);
        }
        //签名步骤四：所有字符转为大写
        return strtoupper($string);
    }

    /**
     * 格式化参数格式化成url参数
     */
    protected function dataToUrl() {
        $buff = "";
        foreach($this->data as $k => $v) {
            if($k != "sign" && $v != "" && !is_array($v)) {
                $buff .= $k."=".$v."&";
            }
        }

        return trim($buff, "&");
    }

    /**
     * 输出xml字符
     **/
    protected function toXml() {
        if(!is_array($this->data) || count($this->data) <= 0) {
            Err::add('数组数据异常！');
            return false;
        }

        $xml = "<xml>";
        foreach($this->data as $key => $val) {
            if(is_numeric($val) || is_string($val)) {
                $xml .= "<".$key.">".$val."</".$key.">";
            } else {
                $xml .= "<".$key."><![CDATA[".$val."]]></".$key.">";
            }
        }
        $xml .= "</xml>";
        return $xml;
    }

    /**
     * xml转数组
     * @param $xml
     * @return mixed
     */
    protected function xmlToArray($xml) {
        if(!$xml){
            Err::add('xml解析失败');
        }
        //将XML转为array
        //禁止引用外部xml实体
        libxml_disable_entity_loader(true);
        return json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
    }

    /**
     * 签名验证
     * @return bool
     */
    protected function verifySign() {
        if(empty($this->data['sign'])) {
            Err::add('支付签名错误');
            return false;
        }

        // 生成sign
        $sign = $this->makeSign();
        if($sign != $this->data['sign']) {
            Err::add('支付签名错误');
            return false;
        }

        return true;
    }


    /**
     * 请求支付接口
     * @param $url
     * @param array $data
     * @param string $mth
     * @return bool
     * @throws Exception
     */
    public function request($url, $data, $mth = 'POST') {
        $res = Curl::getInstance()->xhr($mth, $url, $data);
        $this->data = $this->xmlToArray($res);
        $verifySign = $this->verifySign();
        if($verifySign && $this->data['return_code'] == 'SUCCESS' && $this->data['result_code'] == 'SUCCESS') {
            return $this->data;
        }
        Err::add('支付失败');
        return false;
    }

    /**
     * 配置文件
     * @param $config
     */
    protected function setConfigs($config) {
        $fields = array_keys($this->configs);
        foreach($fields as $v) {
            !empty($configs[$v]) && $this->configs[$v] = $configs[$v];
        }
    }

    /**
     * 设置参数
     * @param $data
     */
    protected function setData($data) {
        $this->data = $data;
        $this->data['nonce_str'] = get_nonce_str(32);
    }
}